#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <signal.h>

#define SVKEY 75        // 消息队列的键值
#define REQ 1           // 请求消息类型
#define SHM_SIZE 512    // 共享内存大小

// 消息结构体定义
struct msg_buf {
    long mtype;         // 消息类型
    pid_t pid;          // 进程ID
};

// 1. 管道通信实现
void pipe_communication() {
    int fd[2];
    pid_t pid;
    char buffer[100];
    
    // 创建管道
    if (pipe(fd) == -1) {
        perror("管道创建失败");
        exit(1);
    }
    
    // 创建子进程
    pid = fork();
    
    if (pid < 0) {
        perror("子进程创建失败");
        exit(1);
    } else if (pid == 0) {
        // 子进程
        close(fd[0]);  // 关闭读端
        
        // 准备消息
        sprintf(buffer, "%d is sending a message to parent", getpid());
        
        // 通过管道发送消息
        write(fd[1], buffer, strlen(buffer) + 1);
        printf("子进程(%d)已发送消息\n", getpid());
        
        close(fd[1]);  // 关闭写端
        exit(0);
    } else {
        // 父进程
        close(fd[1]);  // 关闭写端
        
        // 从管道读取消息
        read(fd[0], buffer, sizeof(buffer));
        printf("父进程收到消息: %s\n", buffer);
        
        close(fd[0]);  // 关闭读端
        wait(NULL);    // 等待子进程结束
    }
}

// 2. 消息队列实现 - 服务端
void message_queue_server() {
    int msgid;
    struct msg_buf msg_recv, msg_send;
    
    // 创建消息队列
    if ((msgid = msgget(SVKEY, IPC_CREAT | 0666)) == -1) {
        perror("消息队列创建失败");
        exit(1);
    }
    
    printf("服务端等待客户端请求...\n");
    
    // 接收请求消息
    if (msgrcv(msgid, &msg_recv, sizeof(pid_t), REQ, 0) == -1) {
        perror("接收消息失败");
        exit(1);
    }
    
    printf("服务端: serving for client %d\n", msg_recv.pid);
    
    // 准备回复消息
    msg_send.mtype = msg_recv.pid;  // 发送给特定客户端
    msg_send.pid = getpid();        // 服务端进程ID
    
    // 发送回复
    if (msgsnd(msgid, &msg_send, sizeof(pid_t), 0) == -1) {
        perror("发送消息失败");
        exit(1);
    }
    
    // 不删除消息队列，让客户端可以接收到消息
}

// 2. 消息队列实现 - 客户端
void message_queue_client() {
    int msgid;
    struct msg_buf msg_send, msg_recv;
    
    // 获取已存在的消息队列
    if ((msgid = msgget(SVKEY, 0)) == -1) {
        perror("获取消息队列失败，请先运行服务端");
        exit(1);
    }
    
    // 准备请求消息
    msg_send.mtype = REQ;
    msg_send.pid = getpid();
    
    // 发送请求
    if (msgsnd(msgid, &msg_send, sizeof(pid_t), 0) == -1) {
        perror("发送消息失败");
        exit(1);
    }
    
    printf("客户端(%d)已发送请求\n", getpid());
    
    // 接收回复
    if (msgrcv(msgid, &msg_recv, sizeof(pid_t), getpid(), 0) == -1) {
        perror("接收消息失败");
        exit(1);
    }
    
    printf("客户端: receive reply from %d\n", msg_recv.pid);
    
    // 删除消息队列
    if (msgctl(msgid, IPC_RMID, NULL) == -1) {
        perror("删除消息队列失败");
    }
}

// 3. 共享内存实现 - 进程A（创建并读取）
void shared_memory_A() {
    int shmid;
    char *shared_memory;
    
    // 创建共享内存
    if ((shmid = shmget(IPC_PRIVATE, SHM_SIZE, IPC_CREAT | 0666)) == -1) {
        perror("创建共享内存失败");
        exit(1);
    }
    
    printf("进程A创建了共享内存，ID: %d\n", shmid);
    
    // 附加共享内存到进程地址空间
    if ((shared_memory = shmat(shmid, NULL, 0)) == (char *) -1) {
        perror("附加共享内存失败");
        exit(1);
    }
    
    // 打印共享内存ID给用户，以便进程B使用
    printf("共享内存创建完成，进程B请使用ID: %d\n", shmid);
    
    // 等待进程B写入数据
    printf("进程A等待进程B写入数据...\n");
    sleep(10);  // 等待最多10秒
    
    // 读取共享内存中的数据
    printf("进程A读取共享内存中的数据: %s\n", shared_memory);
    
    // 分离共享内存
    if (shmdt(shared_memory) == -1) {
        perror("分离共享内存失败");
        exit(1);
    }
    
    // 不删除共享内存，以便进程B完成操作
}

// 3. 共享内存实现 - 进程B（附加并写入）
void shared_memory_B() {
    int shmid;
    char *shared_memory;
    char buffer[SHM_SIZE];
    
    // 获取共享内存ID
    printf("请输入共享内存ID: ");
    scanf("%d", &shmid);
    
    // 附加共享内存到进程地址空间
    if ((shared_memory = shmat(shmid, NULL, 0)) == (char *) -1) {
        perror("附加共享内存失败");
        exit(1);
    }
    
    // 向共享内存写入数据
    printf("请输入要写入共享内存的数据: ");
    scanf(" %[^\n]", buffer);  // 读取一行
    
    strcpy(shared_memory, buffer);
    printf("进程B已写入数据到共享内存\n");
    
    // 分离共享内存
    if (shmdt(shared_memory) == -1) {
        perror("分离共享内存失败");
        exit(1);
    }
    
    // 删除共享内存
    if (shmctl(shmid, IPC_RMID, NULL) == -1) {
        perror("删除共享内存失败");
        exit(1);
    }
}

int main() {
    int choice;
    
    while (1) {
        printf("\n进程通信演示程序\n");
        printf("1. 管道通信（父子进程）\n");
        printf("2. 消息队列 - 服务端\n");
        printf("3. 消息队列 - 客户端\n");
        printf("4. 共享内存 - 进程A（创建）\n");
        printf("5. 共享内存 - 进程B（写入）\n");
        printf("0. 退出\n");
        printf("请选择: ");
        scanf("%d", &choice);
        
        switch (choice) {
            case 1:
                pipe_communication();
                break;
            case 2:
                message_queue_server();
                break;
            case 3:
                message_queue_client();
                break;
            case 4:
                shared_memory_A();
                break;
            case 5:
                shared_memory_B();
                break;
            case 0:
                exit(0);
            default:
                printf("无效选择，请重新输入\n");
        }
    }
    
    return 0;
}